#include <dim-sum/bitops.h>
#include <dim-sum/cpumask.h>
#include <dim-sum/delay.h>
#include <dim-sum/errno.h>
#include <dim-sum/idle.h>
#include <dim-sum/mutex.h>
#include <dim-sum/notifier.h>
#include <dim-sum/percpu.h>
#include <dim-sum/psci.h>
#include <dim-sum/sched.h>
#include <dim-sum/smp.h>
#include <dim-sum/types.h>
#include <dim-sum/cpu.h>

#include <asm/asm-offsets.h>
#include <asm/cacheflush.h>
#include <asm/mmu_context.h>
#include <asm/tlbflush.h>

/**
 * 互斥锁
 * 避免并发的进行CPU热插拨操作
 */
struct mutex mutex_cpu_hotplug = MUTEX_INITIALIZER(mutex_cpu_hotplug);

const DECLARE_BITMAP(cpu_all_bits, MAX_CPUS) = CPU_BITS_ALL;

static DECLARE_BITMAP(cpu_possible_bits, CONFIG_MAX_CPUS) ;
const struct cpumask *const cpu_possible_mask = to_cpumask(cpu_possible_bits);


static DECLARE_BITMAP(cpu_online_bits, CONFIG_MAX_CPUS) ;
const struct cpumask *const cpu_online_mask = to_cpumask(cpu_online_bits);


static DECLARE_BITMAP(cpu_present_bits, CONFIG_MAX_CPUS) ;
const struct cpumask *const cpu_present_mask = to_cpumask(cpu_present_bits);


static DECLARE_BITMAP(cpu_active_bits, CONFIG_MAX_CPUS) ;
const struct cpumask *const cpu_active_mask = to_cpumask(cpu_active_bits);

void mark_cpu_possible(unsigned int cpu, bool possible)
{
	if (possible)
		cpumask_set_cpu(cpu, to_cpumask(cpu_possible_bits));
	else
		cpumask_clear_cpu(cpu, to_cpumask(cpu_possible_bits));
}

void mark_cpu_present(unsigned int cpu, bool present)
{
	if (present)
		cpumask_set_cpu(cpu, to_cpumask(cpu_present_bits));
	else
		cpumask_clear_cpu(cpu, to_cpumask(cpu_present_bits));
}

void mark_cpu_online(unsigned int cpu, bool online)
{
	if (online)
		cpumask_set_cpu(cpu, to_cpumask(cpu_online_bits));
	else
		cpumask_clear_cpu(cpu, to_cpumask(cpu_online_bits));
}

void init_cpu_present(const struct cpumask *src)
{
	cpumask_copy(to_cpumask(cpu_present_bits), src);
}

void init_cpu_possible(const struct cpumask *src)
{
	cpumask_copy(to_cpumask(cpu_possible_bits), src);
}

void init_cpu_online(const struct cpumask *src)
{
	cpumask_copy(to_cpumask(cpu_online_bits), src);
}


struct slave_cpu_data slave_cpu_data;

static int boot_secondary(unsigned int cpu, struct task_desc *idle)
{
	return arch_launch_cpu(cpu, idle);

}

void arch_raise_ipi(const struct cpumask *mask, enum ipi_cmd_type cmd_type)
{
	__smp_raise_ipi(mask, cmd_type);
}

static __maybe_unused __init int slave_init_process(void *unused);
static __maybe_unused  __init void slave_kick_rest(void)
{
	create_process(NULL, slave_init_process,
			NULL,
			"slave_init_process",
			5,
			SCHED_FIFO,
			NULL,
			NULL
		);
}

asmlinkage __init void start_slave(struct slave_cpu_data *slave_cpu_data)
{
	slave_start_arch(slave_cpu_data);
	irq_controller_secondary_init();
	arch_timer_secondary_init();

	/**
	 * TODO：创建用户程序，该进入osek_main
	 */
	// slave_kick_rest();
	local_irq_enable();
	pr_info("从核心%d进入空闲任务运行\n", smp_processor_id());

	cpu_idle();

	BUG();
}

int nr_existent_cpus = 4;

static int __cpu_launch(unsigned int cpu, struct task_desc *idle)
{
	int ret;
	//TODO
	printk("xby_debug in __cpu_launch, %d.\n", cpu);
	slave_cpu_data.stack = task_stack_bottom(idle) + THREAD_START_SP;
	slave_cpu_data.cpu = cpu;
	__flush_dcache_area(&slave_cpu_data, sizeof(slave_cpu_data));

	ret = boot_secondary(cpu, idle);
	if (ret == 0) {
		msleep(10);

		if (!cpu_online(cpu)) {
			pr_crit("CPU%u: failed to come online\n", cpu);
			ret = -EIO;
		}
	} else {
		pr_err("CPU%u: failed to boot: %d\n", cpu, ret);
	}

	slave_cpu_data.stack = NULL;

	return ret;
}

int cpu_launch(unsigned int cpu)
{
	int ret = 0;
	struct task_desc *idle = idle_task(cpu);

	lock_cpu_hotplug();

	if (cpu_online(cpu) || !cpu_present(cpu)) {
		ret = -EINVAL;
		goto unlock;
	}

	ret = __cpu_launch(cpu, idle);
	
unlock:
	unlock_cpu_hotplug();

	return ret;
}

int cpu_offline(unsigned int cpu)
{
	/* TO-DO */
	return 0;
}

int register_cpu_notifier(struct notifier_data *data)
{
	/* TO-DO */
	return 0;
}

void unregister_cpu_notifier(struct notifier_data *data)
{
	/* TO-DO */
}

/**
 * 在进程上下文进行初始化工作。
 * 在开中断的情况下运行，此时可以睡眠。
 */
static __maybe_unused __init int slave_init_process(void *unused)
{
	pr_info("从核心开始运行进程!\n");
	while (1) {
		cpu_do_idle();
	}
	return 0;
}
